//
// Working with UTF-8 encoding.
// Functions 'encode' and 'decode' are taken from http://www.webtoolkit.info/
var Utf8 =
{
    encode: function(string)
    {
        string = string.replace(/\r\n/g,'\n');
        var utftext = '';
        for (var n = 0; n < string.length; n++)
        {
            var c = string.charCodeAt(n);
            if (c < 128)
            {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048))
            {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else
            {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },
    decode: function(utftext)
    {
        var string = '';
        var i = 0;
        var c = c1 = c2 = 0;
        while ( i < utftext.length )
        {
            c = utftext.charCodeAt(i);
            if (c < 128)
            {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224))
            {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else
            {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    },
    encodeToHexString: function(string)
    {
        var utfText = Utf8.encode(string);
        var hexString = '';
        for (var i = 0; i < utfText.length; ++i)
        {
            var hex = utfText.charCodeAt(i).toString(16).toUpperCase();
            if (hex.length == 1)
                hex = '0' + hex;
            hexString += '%' + hex;
        }
        return hexString;
    }
};

//
// Class describes context button that can be dynamically shown on the page
var ContextButton = function()
{
    this._button;
    this._isAppended = false;

    this._init = function()
    {
        this._button = document.createElement('button');
        with (this._button.style)
        {
            margin = '0px';
            padding = '0px';
            width = '14px';
            height = '14px';
            position = 'absolute';
            cursor = 'pointer';
            border = 'none';
            display = 'none';
            background = 'url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) no-repeat';
            zIndex = 4294967296;
            opacity = 1;
        }
        this._button.addEventListener('mouseup', function(e) { /*console.log('button.up');*/ e.stopPropagation(); }, false);
        this._button.addEventListener('mousedown', function(e) { /*console.log('button.down');*/ e.stopPropagation(); }, false);
        this._button.addEventListener('dblclick', function(e) { /*console.log('button.dblclick');*/ e.stopPropagation(); }, false);
        this._button.addEventListener('click',
            function(e)
            {
                //console.log('button.click');
                e.stopPropagation();
                if (menu.isActive)
                {
                    btn.isFrozen = false;
                    menu.close();
                }
                else
                {
                    btn.isFrozen = true;
                    menu.open({ x: btn.pos.x, y: btn.pos.y + 14 });
                }
            },
            false);
    }

    this._init();

    this.isActive = false;
    this.isFrozen = false;
    this.pos = { x: NaN, y: NaN };

    this.updateButtonStyle = function(topPxs, leftPxs, displayStyle)
    {
        with (this._button.style)
        {
            if (topPxs != NaN) top = topPxs + 'px';
            if (leftPxs != NaN) left = leftPxs + 'px';
            display = displayStyle;
        }
    }

    this.setOpacity = function(opacity)
    {
        this._button.style.opacity = opacity;
    }

    this.open = function(pos)
    {
        //console.log('button.open');
        if (!this._isAppended)
        {
            document.body.appendChild(this._button);
            this._isAppended = true;
        }
        this.pos = pos;
        this.updateButtonStyle(this.pos.y, this.pos.x, 'block');
        this.isActive = true;
        this.isFrozen = false;
    }

    this.show = function()
    {
        //console.log('button.show');
        this.updateButtonStyle(this.pos.y, this.pos.x, 'block');
        this.isActive = true;
        this.isFrozen = false;
    }

    this.hide = function()
    {
        //console.log('button.hide');
        this.updateButtonStyle(NaN, NaN, 'none');
        this.isActive = true;
        this.isFrozen = false;
        menu.close();
    }

    this.close = function()
    {
        //console.log('button.close');
        this._button.style.opacity = 1;
        this.pos = { x: NaN, y: NaN };
        this.updateButtonStyle(NaN, NaN, 'none');
        this.isActive = false;
        this.isFrozen = false;
        menu.close();
    }
}

//
// Class describes context menu that can be dymanically shown on the page
var ContextMenu = function()
{
    this._menu = { };
    this._isAppended = false;

    this._addItem = function(name, urlFormat)
    {
        var item = document.createElement('div');
        with (item.style)
        {
            margin = '0px';
            padding = '0px';
            paddingLeft = '4px';
            paddingRight = '6px';
            background = '#FFFFFF';
            color = '#000000';
            font = 'normal 12px verdana,sans-serif';
            textAlign = 'left';
            height = '24px';
            lineHeight = '24px';
            width = 'auto';
            overflow = 'visible';
        }
        var icon
        var iconSrc = getIconUrl(urlFormat);
        if (iconSrc)
        {
            icon = document.createElement('img');
            icon.src = iconSrc;
            icon.addEventListener('mousedown', function(e) { e.preventDefault(); }, false);
        }
        else
        {
            icon = document.createElement('span');
            icon.appendChild(document.createTextNode('\u25BA'));
        }
        with (icon.style)
        {
            float = 'left';
            color = 'rgb(77,77,77)';
            margin = '0px';
            marginTop = '4px';
            marginRight = '6px';
            width = '16px';
            height = '16px';
            lineHeight = '16px';
            textAlign = 'center';
        }
        item.appendChild(icon);
        item.appendChild(document.createTextNode('' + name));
        item.addEventListener('mouseover', function() { item.style.background = '#B6BDD2'; }, false);
        item.addEventListener('mouseout', function() { item.style.background = '#FFFFFF'; }, false);
        item.addEventListener('mousedown',
            function(e)
            {
                //console.log('item.down');
                e.stopPropagation();
                selectedText = trim(selectedText);
                if (selectedText != '')
                {
                    if (urlFormat != '%s')
                    {
                        selectedText = Utf8.encodeToHexString(selectedText.replace(/\s+/g, ' '));
                    }
                    var url = urlFormat.replace('%s', selectedText);
                    if (!url.match(/^[Hh][Tt][Tt][Pp]:\/\//))
                    {
                        url = 'http://' + url;
                    }
                    //console.log('navigate: url = ' + url);
                    sendMessage({ name: 'newTab', param: url });
                    wasMenuClicked = true;
                    selection.collapseToEnd();					
                    btn.close();
                }
            },
            false);
        //console.log('item.add: ' + name + ' (' + urlFormat + ')');
        //console.log('item.icon: ' + iconSrc);
        this._menu.appendChild(item);
    }

    this._addItems = function(options)
    {
        for (var i = 0; i < options.length; ++i)
        {
            menu._addItem(options[i].name, options[i].url);
        }
    }

    this._initMenu = function()
    {
        this._menu = document.createElement('div');
        sendMessage({ name: 'getOptions' }, this._addItems);
        with (this._menu.style)
        {
            margin = '0px';
            padding = '2px';
            border = '1px solid gray';
            background = '#FFFFFF';
            position = 'absolute';
            display = 'none';
            cursor = 'default';
            width = 'auto';
            overflow = 'visible';
            zIndex = 4294967296;
        }
    }

    this._initMenu();

    this.isActive = false;

    this.open = function(point)
    {
        //console.log('menu.open');
        if (!this._isAppended)
        {
            document.body.appendChild(this._menu);
            this._isAppended = true;
        }
        this._menu.style.visibility = 'hidden';
        this._menu.style.display = 'block';

        if (point.x - window.pageXOffset + this._menu.offsetWidth > window.innerWidth - 20)
            point.x -= this._menu.offsetWidth - 14;
        if (point.y - window.pageYOffset + this._menu.offsetHeight > window.innerHeight - 20)
            point.y -= this._menu.offsetHeight + 14;

        this._menu.style.top = point.y + 'px';
        this._menu.style.left = point.x + 'px';
        this._menu.style.visibility = 'visible';
        this.isActive = true;
    }

    this.close = function()
    {
        //console.log('menu.close');
        this._menu.style.top = NaN;
        this._menu.style.left = NaN;
        this._menu.style.display = 'none';
        this.isActive = false;
    }
}


var menu = new ContextMenu();
var btn = new ContextButton();
var selectedText = '';
var wasMenuClicked = false;
var mouseStatus = { pressed: false, pressedEventArgs: {}, pressedAndMoved: false };

document.addEventListener('mouseup',
    function(e)
    {
        //console.log('document.up');
        if (e.button == 0)
        {
            updateContextButton(e, true);
        }
        mouseStatus.pressed = false;
        mouseStatus.pressedAndMoved = false;
    },
    false);

document.addEventListener('mousedown',
    function(e)
    {
        //console.log('document.down');
        if (btn.isActive)
        {
            btn.close();
        }
        mouseStatus.pressed = true;
        mouseStatus.pressedEventArgs = e;
        mouseStatus.pressedAndMoved = false;
    },
    false);

document.addEventListener('dblclick',
    function(e)
    {
        //console.log('document.dblclick');
        updateContextButton(e, false);
        mouseStatus.pressed = false;
        mouseStatus.pressedAndMoved = false;
    },
    false);

document.addEventListener('mousemove',
    function(e)
    {
        if (btn.isActive && !btn.isFrozen)
        {
            var distance = Math.sqrt(Math.pow(e.pageX - btn.pos.x, 2) + Math.pow(e.pageY - btn.pos.y, 2));
            if (distance < 30)
                btn.setOpacity(1);
            else if (30 < distance && distance < 530)
                btn.setOpacity(-7.0 * distance / 5000 + 1.042);
            else
                btn.setOpacity(0.3);
        }
        if (mouseStatus.pressed && !mouseStatus.pressedAndMoved)
        {
            mouseStatus.pressedAndMoved = true;
        }
    },
    false);

document.addEventListener('keydown',
    function(e)
    {
        //console.log('dicument.keyDown: code=' + e.keyCode);
        if ((e.keyCode == 27 && e.shiftKey == false && e.ctrlKey == false && e.altKey == false) ||
            (trim(window.getSelection().toString()) == ''))
        {
            if (btn.isActive) btn.close();
        }
    },
    false);


function updateContextButton(eventArgs, checkMouseStatus)
{
    var target = eventArgs.target;
    var pressedTarget = mouseStatus.pressedEventArgs.target;
    var selection = window.getSelection();
    selectedText = selection.toString();

    //console.log('selection: target = ' + target + ' (' + target.nodeName + ')');
    //console.log('selection: pressedTarget = ' + pressedTarget + ' (' + pressedTarget.nodeName + ')');
    //console.log('selection: text = "' + selectedText + '"');

    if (target.nodeName != 'TEXTAREA' && target.nodeName != 'INPUT' &&
        pressedTarget.nodeName != 'TEXTAREA' && pressedTarget.nodeName != 'INPUT')
    {
        if (!wasMenuClicked && trim(selectedText) != '' &&
            (!checkMouseStatus || mouseStatus.pressedAndMoved))
        {
            var buttonShift;
            if (checkMouseStatus)
            {
                var d = 20;
                var horShift = eventArgs.pageX - mouseStatus.pressedEventArgs.pageX;
                var verShift = eventArgs.pageY - mouseStatus.pressedEventArgs.pageY;
                buttonShift = (verShift > d) ? { x: 5, y: 5 } :
                    (verShift < -d || horShift < 0) ? { x: -15, y: -20 } : { x: 5, y: 5 };
            }
            else
            {
                buttonShift = { x: 5, y: 10 };
            }			
            btn.open({ x: eventArgs.pageX + buttonShift.x, y: eventArgs.pageY + buttonShift.y });
        }
        else
        {
            btn.close();
        }
    }
    if (wasMenuClicked)
    {
        selection.collapseToEnd();
        wasMenuClicked = false;
    }
}

function getIconUrl(urlFormat)
{
    var url = urlFormat.replace('%s', '').replace(/^\/+|\/+$/g, '');
    if (url != '')
    {
        var hostIndex = url.match(/^[Hh][Tt][Tt][Pp]:\/\//) ? 2 : 0;
        var iconUrl = 'http://' + url.split('/')[hostIndex] + '/favicon.ico';
        return iconUrl;
    }
    return null;
}

function trim(string)
{
    return string.replace(/^\s+|\s+$/g, '');
}

function sendMessage(message, callback)
{
    var port = chrome.extension.connect({ name: 'contextsearch' });
    if (callback)
    {
        port.onMessage.addListener(callback);
    }
    if (message)
    {
        port.postMessage(message);
    }
}
